Repository navigation
AVX2 kernel for float64 Log: 1.55x faster than NumPy - #96
Merged
Merged
Conversation
float64 Log was 1.23x slower than NumPy at 16 Ki on Zen 3: NumPy calls glibc's scalar log, the same optimized-routines log.c ours ports, so the gap was our scalar Go. logAVX2 runs Log's table path on four doubles at a time with Log's operations in its order: its one FMA as VFMADD231PD, every other product and sum as a separate VMULPD/VADDPD, as Go compiles them on amd64. invc and logc are gathered (VPGATHERQQ); the exponent, for which AVX2 has no 64-bit arithmetic shift or int64 conversion, is built exactly with the 2^52 magic constant. Blocks with a non-positive-normal lane or a lane within 2^-4 of 1 are handed back to the Go loop (logScalar). TestLogAVX2MatchesGo holds it to the Go loop bit for bit; dropping the kd*ln2lo term fails it (TestLogULP alone would not). Fenced; layout tested. 16 Ki: 83.9 -> 44.4 us, 1 Mi: 5.06 -> 2.59 ms (abbench, 8 rounds, p = 0.000); NumPy 69 us and 4.4 ms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 7, 2026
tannevaled
added a commit
that referenced
this pull request
Nov 7, 2026
…xp, log (K1) (#97) * kernels: import go-simd/floats for sum, elementwise, extrema, clip, exp, log (K1) K1 of the go-scikit performance campaign (go-scikit/roadmap#27): ndarray now imports go-simd/floats v0.9.0 and deletes its copies. - exp, log, and the correctly rounded float32 exp/log (with the AVX2 kernels of #94/#96 and the NEON kernel of #95, now go-simd/floats#29): deleted here (Go sources, tests, generator kernels, assembly); Exp, Log, Exp32, Log32 and their P forms call floats. - amd64 and arm64: the sum/sqrt/max/min/add/sub/mul/div kernels are floats'; Clip is floats.ClampScalar. Max/Min resolve a zero result with the serial oracle (floats leaves the sign of zero unspecified). - Sum is floats.SumPairwise on every architecture: NumPy's order, bit-identical to np.sum; serial at every size. A changed last bit is the fix; the golden values of TestSumMatchesNumPy are NumPy 2.5.4's. - Not moved: the float32 elementwise/sqrt/extremum/AddBlocks8F32 kernels, the dot inside GEMM/Dot1D, and the ppc64le, loong64, riscv64 and s390x kernels (floats has none for them yet). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * kernels: test the parallel exp/log forms and Log10 (100% coverage gate) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
float64
Loggets an AVX2 kernel and goes from 1.23× slower than NumPy to 1.55× faster on one Zen 3 core. This closes the last open defect indocs/perf.md's Exp/Log section.Why it was slower: NumPy's 4.2 ns per element is glibc's scalar log, which is optimized-routines'
log.c, the codeLogports. The gap was our scalar Go.logAVX2runsLog's table path on 4 doubles at a time, withLog's operations in its order.r = z·invc − 1, isVFMADD231PD. Every other product and sum is a separateVMULPD/VADDPD, which is how Go compiles them on amd64. The result is bit-identical to the Go loop.invcandlogcare gathered from the 16-byte table rows (VPGATHERQQ).kd = int64(tmp)>>52is built exactly: the float64 with bits0x4330000000000000 | (u ^ 0x800), minus 2⁵² + 2048.Log's polynomial path), is handed back to the Go loop (logScalar) before anything is stored.Tests:
TestLogAVX2MatchesGoholds it to the Go loop bit for bit: 300 000 inputs covering every exponent and table index, values near 1, the special values, every length 0–70, and in place.+ kd·ln2loterm fails it, whichTestLogULPalone would not catch (worst 0.5078 ULP, unchanged).logEntryare tested.Log, one Zen 3 core, load ≤ 3.7np.log(a)That is −47% and −49% (
abbench, 8 interleaved rounds, p = 0.000), both sides allocating. The README now lists float64Logunder "faster".SECURITY.mdhas the audit entry.🤖 Generated with Claude Code